package main

// 创建新游戏、玩家队列，顺序，资金，底注，发牌，轮流下注

import (
	"fmt"
	"log"
	"math/rand"
	"sync"

	uuid "github.com/satori/go.uuid"
)

type Record struct {
	PlayerId string
	Action   string
	Bet      int
	Comment  string
}

type Game struct {
	Id     int
	RoomId int
	State  string // 游戏状态机：CREATED，PLAYING，DONE, ERROR

	Players     []Player
	FirstPlayer int // 第一个下注：第一次随机，以后是上一轮胜者的下家
	CurrPlayer  int // 当前玩家的顺序

	WinnerIdx  int // 本轮赢家
	NumWinners int // 当前牌局还剩余多少玩家

	// 牌面信息

	// 下注记录
	Records   []Record
	MinAmount int // 最小下注额
	PrizePool int // 奖池

	// 事件通道
	event chan Event
}

func (g *Game) serve(room *Room) {

	defer func() {
		log.Println("DEFER: 游戏线程结束: ", g.Id)
		if g.State != "ERROR" {
			log.Println("DEFER: 开启新游戏线程")
			go room.newGame(g)
		}
	}()

	// 游戏工作线程、事件机制

	// 开局
	g.State = "NEWGAME"
	success := g.NewGame() // block

	if !success {
		log.Println("游戏结束。未完成底注！")
		g.State = "GAMEOVER"
		return
	}

	// 开始轮流下注
	g.State = "PLAYING"
	for {

		if g.State != "PLAYING" {
			log.Println("ERROR: 游戏已结束！State=", g.State)
			return
		}

		go g.Players[g.CurrPlayer].YourTurn(g)

		log.Println("Waiting for action from ", g.Players[g.CurrPlayer].Id)
		e := <-g.event
		log.Println("Action", e)

		if g.Players[g.CurrPlayer].Id != e.playerId {
			g.State = "ERROR"
			fmt.Println(g)
			log.Println("ERROR: 玩家动作顺序错误")
			return
		}

		g.Update(e)
		if g.State == "ERROR" {
			log.Println("游戏异常结束")
			return
		}

		if g.State == "END" {
			log.Println("游戏正常结束")
			return
		}

		// inform next player
		g.CurrPlayer++
		if g.CurrPlayer >= len(g.Players) {
			g.CurrPlayer = 0
		}

	}
}

func (g *Game) Describe(playerId string) string {
	return "你现在在一个牌局中，历史出牌记录"
}

func (g *Game) GetPlayer(playerId string) *Player {
	for idx := range g.Players {
		if g.Players[idx].Id == playerId {
			return &g.Players[idx]
		}
	}
	return nil
}

func (g *Game) Update(e Event) {

	// 查找玩家
	p := g.GetPlayer(e.playerId)
	if p == nil {
		g.State = "ERROR"
		fmt.Println(g)
		log.Fatalln("找不到玩家", g.Id, e.playerId)
		return
	}

	// 玩家已经弃牌，轮空处理
	if p.HasFold {
		g.Records = append(g.Records, Record{e.playerId, "SKIP", 0, "-"})
		return
	}

	// 根据Event操作
	if e.bet < 0 {
		// -1: 弃牌
		p.HasFold = true
		g.Records = append(g.Records, Record{e.playerId, "FOLD", -1, e.reason})

	} else {
		// >0 : 下注金额处理
		amount := max(g.MinAmount, e.bet)

		if p.Chips-p.Invest < amount {
			log.Println("ERROR：玩家钱不够，强制弃牌")
			p.HasFold = true
			g.Records = append(g.Records, Record{e.playerId, "FOLD", -99, e.reason + "筹码不足，强制弃牌"})

		} else {
			p.Invest += amount
			g.PrizePool += amount
			g.Records = append(g.Records, Record{e.playerId, "BET", amount, e.reason})
			g.MinAmount = amount
		}

	}

	// 未弃牌的玩家
	winners := []int{}
	for idx, p := range g.Players {
		if !p.HasFold {
			winners = append(winners, idx)
		}
	}

	g.NumWinners = len(winners)
	if len(winners) <= 0 {
		log.Println("ERROR：所有玩家都已弃牌")
		g.State = "GAMEOVER"
		return
	}

	// 是否只剩下2个玩家：可以开牌
	if len(winners) == 2 {
		if compare(
			g.Players[winners[0]].Card,
			g.Players[winners[1]].Card) > 0 {
			winners = winners[:1]
		} else {
			winners = winners[1:]
		}
	}

	// 是否只剩下1个玩家：其他玩家都已弃牌
	if len(winners) == 1 {
		g.Players[winners[0]].Profit += g.PrizePool
		g.NumWinners = 1
		g.WinnerIdx = winners[0]
		g.PrizePool = 0
		g.State = "END"
	}

}

func (g *Game) NewGame() bool {
	// 开始新的一局。
	// 玩家有两个选项：1下底注 2结账离开
	// 对于AI来说，只有钱不够的情况下才离开。
	// 如果任一玩家离开，底注过程失败，返回false，游戏结束(GAMEOVER)

	log.Println("游戏开局", g.Id)
	wg := new(sync.WaitGroup)

	N := len(g.Players)
	wg.Add(N)

	result := true

	for idx, p := range g.Players {
		go func(i int) {
			defer wg.Done()

			ante := g.Players[i].DoAnte(g)
			g.Players[i].Ante = ante

			if ante < 0 {
				// 放弃下注，或异常情况
				log.Panicln("玩家底注异常:", p.Id, p.Name, ante)
				g.Players[i].HasQuit = true
				result = false
				return

			} else if ante == 0 {
				log.Println("玩家离场:", p.Id, p.Name)
				g.Players[i].HasQuit = true
				result = false
				return

			} else {
				log.Println("玩家底注:", p.Id, p.Name, ante)
				return
			}

		}(idx)

	}

	log.Println("等待所有玩家下底注...")
	wg.Wait()
	log.Println("等待结束。result = ", result)

	if result {
		// 正常底注
		for idx, p := range g.Players {
			g.Players[idx].Invest += p.Ante
			g.PrizePool += p.Ante
		}
	}

	return result

}

func (g *Game) Setup() {

	log.Println("游戏初始化，GameId=", g.Id)

	for idx, _ := range g.Players {
		g.Players[idx].action = make(chan Event)
		g.Players[idx].choice = make(chan string)
		g.Players[idx].Timeout = 60
		g.Players[idx].Invest = 0
		g.Players[idx].Profit = 0
	}

	g.Records = []Record{}
	g.event = make(chan Event)

	g.DealCard()
	g.FirstPlayer = 0
	g.CurrPlayer = 0
	g.WinnerIdx = -1
	g.NumWinners = len(g.Players)

	g.MinAmount = 10
	g.PrizePool = 0

}

func (g *Game) Initialize(c []Candidate) {

	// Candidate升级为Player
	g.Players = []Player{}
	for _, m := range c {

		var p = Player{
			Candidate: m,
		}

		if !p.IsHuman {
			p.Id = uuid.NewV1().String()
			p.Name = "AI." + botName()
		}

		p.Chips = 1000
		g.Players = append(g.Players, p)
	}

}

func (g *Game) DealCard() {
	numPlayers := len(g.Players)

	cards := deal(numPlayers, 3)

	for idx := range g.Players {
		g.Players[idx].Card = cards[idx]
		fmt.Println(g.Players[idx].Id, cards[idx], str2card(cards[idx]), value(cards[idx]))
	}

}

func botName() string {
	_list := [...]string{
		"刘备", "关羽", "张飞", "诸葛亮", "赵云", "马超", "黄忠", "曹操", "曹丕", "曹植", "曹冲", "司马懿",
		"司马昭", "司马炎", "孙坚", "孙策", "孙权", "周瑜", "鲁肃", "吕蒙", "陆逊", "黄盖", "程普", "甘宁", "太史慈",
		"张辽", "徐晃", "张郃", "于禁", "乐进", "典韦", "许褚", "郭嘉", "荀彧", "荀攸", "贾诩", "庞统", "法正", "姜维",
		"邓艾", "钟会", "庞德", "颜良", "文丑", "华雄", "吕布", "陈宫", "貂蝉", "董卓", "王允", "袁绍", "袁术", "公孙瓒",
		"刘表", "刘璋", "马腾", "韩遂", "孟获", "祝融夫人", "大乔", "小乔", "甄宓", "蔡文姬", "孙皓", "诸葛瑾", "诸葛恪",
		"徐庶", "蒋干", "杨修", "陈琳", "孔融", "田丰", "沮授", "审配", "郭图", "逢纪", "许攸", "蒯良", "蒯越", "马谡",
		"王平", "魏延", "关平", "周仓", "廖化", "马岱", "张苞", "关兴", "曹仁", "曹洪", "夏侯惇", "夏侯渊", "曹真",
		"曹休", "夏侯霸", "郭淮", "孙礼", "邓芝", "费祎", "董允", "向宠", "张翼", "张嶷", "姜维", "钟繇", "钟会", "陈群",
		"贾逵", "满宠", "曹爽", "何晏", "夏侯玄", "李典", "李通", "吕虔", "毛玠", "刘晔", "刘馥", "朱灵", "路昭", "车胄",
		"韩浩", "史涣", "韩当", "程普", "黄盖", "祖茂", "凌统", "丁奉", "徐盛", "潘璋", "马忠", "蒋钦", "周泰", "陈武",
		"董袭", "甘宁", "太史慈", "孙策", "孙坚", "孙翊", "孙匡", "孙朗", "孙静", "孙乾", "简雍", "糜竺", "糜芳", "孙峻",
		"孙綝", "诸葛诞", "文鸯", "文钦", "毋丘俭", "诸葛瞻", "诸葛尚", "黄皓", "谯周", "刘封", "孟达", "杨仪", "高翔",
		"吴班", "冯习", "傅佥", "傅彤", "霍峻", "罗宪", "王甫", "赵累", "马良", "伊籍", "陈震", "董和", "费诗", "李严",
		"刘巴", "吕凯", "吕乂", "杨颙", "杨戏", "王连", "张裔", "秦宓", "许靖", "谯周", "郤正", "张松", "法正", "孟达",
		"刘封", "杨仪", "魏延", "关兴", "张苞"}

	idx := rand.Intn(len(_list))
	return _list[idx]
}
